home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / EventLib / EventLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-18  |  26.3 KB  |  936 lines  |  [TEXT/KAHL]

  1. /*
  2.     94/01/12 aih
  3.     - Another major revision to put event handler functions into some
  4.     sort of order by breaking them up into functional groups.
  5.     
  6.     94/01/11 aih
  7.     - Menu command handlers are passed a "const MenuPickType *pick"
  8.     parameter instead of a "MenuPickType mc" parameter. This is
  9.     marginally more efficient and it's more consistent with
  10.     structure parameters in the rest of the code (which are nearly
  11.     all passed by reference).
  12.     
  13.     94/01/03 aih
  14.     - After each event handler is called, if available memory has dropped
  15.     below a certain threshold then segments are unloaded. This is done
  16.     since calling an event handler typically loads the segment containing
  17.     the code for the handler, often along with several other segments
  18.     needed by the handler, but once the handler has finished executing
  19.     those segments are no longer needed.
  20.     
  21.     93/12/21 aih
  22.     - major revisions to remove need to predefine an event kind,
  23.     which anyway wasn't used for anything important; this will make
  24.     it easier to add plug-in modules without having to recompile
  25.     existing modules
  26.     
  27.     93/12/17 aih
  28.     - changed to use LLPtrLib to avoid duplicating linked-list code
  29.     
  30.     93/10/18 aih
  31.     - put file through another major revision to use tables
  32.     containing pointers to event handlers. this instead of using
  33.     switch statements and direct calls to the event handlers. this
  34.     will help shrink object code size by allowing the linker to strip
  35.     out unused libraries, and also remove the need to modify
  36.     this file every time a new event kind is added.
  37.     
  38.     93/03/?? aih
  39.     - modified event library to use switch statements instead of previous
  40.     inefficient method of table lookup for event handlers */
  41.     
  42. #include <stdlib.h>
  43. #include <limits.h>
  44. #include <string.h>
  45. #include <AppleEvents.h>
  46. #include <DiskInit.h>
  47. #include "ByteLib.h"
  48. #include "DialogLib.h"
  49. #include "DrawLib.h"
  50. #include "EventLib.h"
  51. #include "EventPrivateLib.h"
  52. #include "KeyLib.h"
  53. #include "LowMemLib.h"
  54. #include "MacLib.h"
  55. #include "MathLib.h"
  56. #include "MemoryLib.h"
  57. #include "MenuLib.h"
  58. #include "NotifyLib.h"
  59. #include "RectangleLib.h"
  60. #include "ResourceConstantsLib.h"
  61. #include "ScreenLib.h"
  62. #include "SegmentLib.h"
  63. #include "SystemWindowLib.h"
  64. #include "WindowLib.h"
  65.  
  66. /*----------------------------------------------------------------------------*/
  67. /* constants and globals */
  68. /*----------------------------------------------------------------------------*/
  69.  
  70. /* Unload segments if the difference between the total free space and
  71.     the maximum contiguous block is at least this amount. */
  72. #define UNLOAD_THRESHOLD        (2048)
  73.  
  74. /* Unload segments no more often than this interval. */
  75. #define UNLOAD_INTERVAL            (20 * TICKS_SEC)
  76.  
  77. /* interval between memory low warnings */
  78. #define WARN_INTERVAL            (TICKS_MIN)
  79.  
  80. Boolean gInBackground;            /* true if application is in the background */
  81.  
  82. struct {
  83.     Boolean appleEvent;            /* true if executing an Apple event */
  84.     Boolean exit;                    /* if true then the event loop is exited */
  85.     MemoryWarningType warned;    /* highest warning level for which a
  86.                                             low-memory alert was displayed */
  87.     TicksType lastwarned;        /* when last low-memory alert was displayed */
  88. } gEvent;
  89.  
  90. /*----------------------------------------------------------------------------*/
  91. /* low memory */
  92. /*----------------------------------------------------------------------------*/
  93.  
  94. /* The heap usually becomes fragmented when a segment is unloaded while
  95.     there are still locked segments below it in the heap (segments are
  96.     loaded at the top of the heap). Since MoveHHi won't move newly loaded
  97.     segments past a locked segment, the space now vacated by the unloaded
  98.     segment will not be filled as more segments are loaded. Therefore,
  99.     we unload all of the segments at the top of the event loop to
  100.     ensure that there are no segments in use that could fragment the heap.
  101.     
  102.     The heap is considered fragmented if the total free space is significantly
  103.     greater than the maximum contiguous block that could be allocated.
  104.     To prevent the application from constantly unloading segments,
  105.     when unloading them has no effect on fragmentation, we use
  106.     a delay of several seconds. */
  107. static void DefragmentHeap(void)
  108. {
  109.     static TicksType lastUnload;
  110.     long total, contig;
  111.     
  112.     PurgeSpace(&total, &contig);
  113.     if (contig + UNLOAD_THRESHOLD < total &&
  114.          TickCount() - lastUnload > UNLOAD_INTERVAL)
  115.     {
  116.         SegmentsUnload();
  117.         lastUnload = TickCount();
  118.     }
  119. }
  120.  
  121. /* check for low memory situation and disable features (in order
  122.     of severity of memory shortage) until there's enough memory */
  123. static void EvtMemoryLow(MemoryWarningType warning)
  124. {
  125.     static Boolean recovering;         /* prevent recursive calls */
  126.     static TicksType lastfree;        /* time when we last tried to free memory */
  127.     Boolean canfail = false;
  128.     
  129.     if (recovering) return;
  130.     TRY {
  131.  
  132.         recovering = true;
  133.         
  134.         /* recovering from a low-memory situation is a critical operation
  135.             which shouldn't be prevented due to lack of some memory, since
  136.             we're trying to make memory available */
  137.         canfail = MemCanFail(false);
  138.         
  139.         /* terminate immediately, before more than just this application crashes */
  140.         if (warning >= MEM_WARNING_FATAL)
  141.             ExitToShell();
  142.         
  143.         /* Warn user. There are several warning messages, each corresponding to
  144.             a memory warning level. Each message is displayed only once, and if
  145.             a more severe message has already been displayed then less severe
  146.             messages are skipped. Since memory usage may be borderline, resulting
  147.             in low-memory situations occuring very frequently, we avoid
  148.             displaying warning messages unless either the situation worsens
  149.             or some time (e.g., 1 minute) has passed since the last warning. */
  150.         if (gEvent.warned < warning ||
  151.                 (! gEvent.warned && TickCount() - gEvent.lastwarned > WARN_INTERVAL))
  152.         {
  153.             switch (warning) {
  154.             case MEM_WARNING_LOW:
  155.                 NotifyCaution(RLS_ALERT_MEMORY_LOW);
  156.                 break;
  157.             case MEM_WARNING_VERY_LOW:
  158.                 NotifyCaution(RLS_ALERT_MEMORY_VERY_LOW);
  159.                 break;
  160.             case MEM_WARNING_CRITICAL:
  161.                 NotifyCaution(RLS_ALERT_MEMORY_CRITICAL);
  162.                 break;
  163.             }
  164.             gEvent.lastwarned = TickCount();
  165.             gEvent.warned = warning;
  166.         }
  167.         
  168.         /* Try to free up enough memory. A delay is used so that the
  169.             application doesn't spend all of its time trying to free
  170.             memory if none is available. */
  171.         if (TickCount() - lastfree > TICKS_SEC) {
  172.             const EventTableType *table;
  173.             short i, ntable;
  174.             
  175.             UnloadScrap();
  176.             ntable = EventTableCount();
  177.             for (i = 0; i < ntable; i++) {
  178.                 table = EventTableGet(i);
  179.                 if (table->objectType.memorylow) {
  180.                     table->objectType.memorylow();
  181.                 }
  182.             }
  183.             lastfree = TickCount();
  184.         }
  185.         MemCanFail(canfail);
  186.         recovering = false;
  187.     } CATCH {
  188.         recovering = false;
  189.     } ENDTRY;
  190. }
  191.  
  192. /* check memory status */
  193. static void EvtMemoryCheck(void)
  194. {
  195.     if (MemWarning()) {
  196.         /* tell user about the low memory situation and take steps to correct it */
  197.         EvtMemoryLow(MemWarning());
  198.     }
  199.     else if (TickCount() - gEvent.lastwarned > WARN_INTERVAL) {
  200.         /* reset warning indicator so user will be alerted
  201.             next time memory is low */
  202.         gEvent.warned = MEM_WARNING_NONE;
  203.     }
  204. }
  205.  
  206. /*----------------------------------------------------------------------------*/
  207. /* menu events */
  208. /*----------------------------------------------------------------------------*/
  209.  
  210. /* dispatch a menu command */
  211. static void EvtMenu(const MenuPickType *pick)
  212. {
  213.     const EventTableType *table;
  214.     Boolean handled = false;
  215.     short i, ntable;
  216.     
  217.     require(MenuEnabled(pick->menu, 0) && MenuEnabled(pick->menu, pick->item));
  218.     handled = SystemDoMenu(pick);
  219.     table = FocusTable();
  220.     if (! handled && table && table->focusObject.menu)
  221.         handled = table->focusObject.menu(FocusObject(), pick);
  222.     ntable = EventTableCount();
  223.     for (i = 0; ! handled && i < ntable; i++) {
  224.         table = EventTableGet(i);
  225.         if (table->objectType.menu)
  226.             handled = table->objectType.menu(pick);
  227.     }
  228. }
  229.  
  230. /*----------------------------------------------------------------------------*/
  231. /* adjust menu, cursor, and sleep period */
  232. /*----------------------------------------------------------------------------*/
  233.  
  234. /* adjust the menus and redraw menu bar if necessary */
  235. static void EvtAdjustMenu(void)
  236. {
  237.     const EventTableType *table;
  238.     short i, ntable;
  239.     Boolean canfail;
  240.     long flags;
  241.     
  242.     /* When memory is low, even adjusting the menus could fail since it may
  243.         require some allocation. To prevent an infinite sequence of "Out of
  244.         memory alerts" (which are generated in response to the out of memory
  245.         alert itself, as well as simply clicking in the menu bar), we set
  246.         the canfail flag to false. */
  247.     canfail = MemCanFail(false);
  248.     flags = MenuBarState();
  249.     MenuAdjust();
  250.     SystemAdjustMenu();
  251.     table = FocusTable();
  252.     if (table && table->focusObject.adjustmenu)
  253.         table->focusObject.adjustmenu(FocusObject());
  254.     ntable = EventTableCount();
  255.     for (i = 0; i < ntable; i++) {
  256.         table = EventTableGet(i);
  257.         if (table->objectType.adjustmenu)
  258.             table->objectType.adjustmenu();
  259.     }
  260.     if (flags != MenuBarState()) {
  261.         HiliteMenu(0);
  262.         DrawMenuBar();
  263.     }
  264.     MemCanFail(canfail);
  265. }
  266.  
  267. /* adjust the cursor */
  268. static RgnHandle EvtAdjustCursor(void)
  269. {
  270.     static RgnHandle cursorRgn;
  271.     EventType *list;
  272.     Boolean arrow;
  273.     Point where;
  274.  
  275.     arrow = true;
  276.     if (! cursorRgn) {
  277.         cursorRgn = NewRgn();
  278.         FailNIL(cursorRgn);
  279.     }
  280.     CopyRgn(ScreenRgnAll(), cursorRgn);
  281.     GetMouse(&where);
  282.     LocalToGlobal(&where);
  283.     if (SystemIsActive())
  284.         arrow = false;
  285.     for (list = FocusList(); arrow && list; list = EventNext(list)) {
  286.         if (list->table->focusWindow.adjustcursor)
  287.             arrow = ! list->table->focusWindow.adjustcursor(list->object, where, cursorRgn);
  288.     }
  289.     if (arrow)
  290.         DrawCursor(arrowCursor);
  291.     return(cursorRgn);
  292. }
  293.  
  294. /* return time to sleep to pass to WNE */
  295. static TicksType EvtAdjustSleep(void)
  296. {
  297.     EventType *list = NULL;
  298.     TicksType sleep = LONG_MAX;
  299.     
  300.     if (SystemIsActive())
  301.         sleep = 0;
  302.     for (list = FocusList(); sleep > 0 && list; list = EventNext(list)) {
  303.         if (list->table->focusWindow.adjustsleep)
  304.             sleep = min(sleep, list->table->focusWindow.adjustsleep(list->object));
  305.     }
  306.     return(sleep);
  307. }
  308.  
  309. /*----------------------------------------------------------------------------*/
  310. /* null event */
  311. /*----------------------------------------------------------------------------*/
  312.  
  313. /* handle a null event */
  314. static void EvtNullEvent(EventRecord *event)
  315. {
  316.     const EventTableType *table;
  317.     
  318.     table = FocusTable();
  319.     if (table && table->focusObject.idle)
  320.         table->focusObject.idle(FocusObject());
  321. }
  322.  
  323. /*----------------------------------------------------------------------------*/
  324. /* closing windows */
  325. /*----------------------------------------------------------------------------*/
  326.  
  327. static void EvtClose(WindowPtr window)
  328. {
  329.     EventType *list;
  330.     
  331.     for (list = WinObjects(window); list; list = EventNext(list)) {
  332.         if (list->table->window.close) {
  333.             list->table->window.close(list->object);
  334.             break;
  335.         }
  336.     }
  337. }
  338.  
  339. static void EvtCloseAll(WindowPtr window)
  340. {
  341.     if (window) {
  342.         EvtCloseAll((WindowPtr) ((WindowPeek) window)->nextWindow);
  343.         if (WinHasExtra(window))
  344.             EvtClose(window);
  345.     }
  346. }
  347.  
  348. /*----------------------------------------------------------------------------*/
  349. /* mousedown and mouseup */
  350. /*----------------------------------------------------------------------------*/
  351.  
  352. /* tell application which object was typed in or clicked in, or use nil
  353.     object and id if clicked outside of object (e.g., text field is being
  354.     closed, window is being deactivated) */
  355. static void EvtClicked(WindowPtr window, EventObjectType object, EventIDType id)
  356. {
  357.     EventType *list;
  358.     
  359.     if (window) {
  360.         if (id)
  361.             DlgClick(window, id);
  362.         for (list = WinObjects(window); list; list = EventNext(list)) {
  363.             if (list->table->focusWindow.clicked)
  364.                 list->table->focusWindow.clicked(list->object, object, id);
  365.         }
  366.     }
  367. }
  368.  
  369. static void EvtMouseDownInMenuBar(EventRecord *event)
  370. {
  371.     long menu = 0;
  372.     
  373.     EvtClicked(FocusWindow(), NULL, 0);
  374.     EvtAdjustMenu();
  375.     menu = MenuSelect(event->where);
  376.     if (HiWord(menu)) {
  377.         MenuPickType pick = MenuPick(HiWord(menu), LoWord(menu));
  378.         EvtMenu(&pick);
  379.         HiliteMenu(0);
  380.     }
  381. }
  382.  
  383. static void EvtMouseDownInSystemWindow(EventRecord *event, WindowPtr window)
  384. {
  385.     SystemClick(event, window);
  386. }
  387.  
  388. static void EvtMouseDownInContent(EventRecord *event, WindowPtr window)
  389. {
  390.     EventType *list = NULL;
  391.     EventType *found = NULL;
  392.     Point where = event->where;
  393.     Boolean click = true;
  394.     
  395.     /* We need to make sure the window is an application window since
  396.         it may be a "Looking for LaserWriter..." dialog displayed by
  397.         the Print Manager. */
  398.     if (WinHasExtra(window)) {
  399.     
  400.         /* Select window if it isn't the front window in its layer. Floating
  401.             windows will process the click even if they weren't the front window,
  402.             while all other windows will be selected but will not process
  403.             the click. */
  404.         if (window != FocusWindow() &&
  405.              (WinLayer(window) != WIN_LAYER_FLOAT ||
  406.               window != WinFirstVisible(WIN_LAYER_FLOAT) ||
  407.               SystemIsActive()))
  408.         {
  409.             if (WinLayer(window) != WIN_LAYER_FLOAT)
  410.                 click = false;
  411.             WinSelect(window);
  412.         }
  413.         if (click) {
  414.             /* send the event to the object containing the point clicked */
  415.             GlobalToPort(&where, window);
  416.             for (list = WinObjects(window); list && ! found; list = EventNext(list)) {
  417.                 if (list->table->focusWindow.within) {
  418.                     if (list->table->focusWindow.within(list->object, where))
  419.                         found = list;
  420.                 }
  421.             }
  422.             if (found && found->table->focusWindow.mousedown) {
  423.                 if (! found->table->focusWindow.mousedown(found->object, event))
  424.                     found = NULL;
  425.             }
  426.             EvtClicked(window, found ? found->object : NULL, found ? found->id : 0);
  427.         }
  428.     }
  429. }
  430.  
  431. static void EvtMouseDownInDrag(EventRecord *event, WindowPtr window)
  432. {
  433.     if ((event->modifiers & cmdKey) == 0)
  434.         WinSelect(window);
  435.     WinDrag(window, event->where);
  436. }
  437.  
  438. static void EvtMouseDownInGrow(EventRecord *event, WindowPtr window)
  439. {
  440.     Rect oldSize, newSize;
  441.     
  442.     WinPortRect(window, &oldSize);
  443.     WinGrow(window, event->where);
  444.     WinPortRect(window, &newSize);
  445.     WinResize(window, RectWidth(&newSize) - RectWidth(&oldSize),
  446.                             RectHeight(&newSize) - RectHeight(&oldSize));
  447. }
  448.  
  449. static void EvtMouseDownInGoAway(EventRecord *event, WindowPtr window)
  450. {
  451.     if (TrackGoAway(window, event->where))
  452.         EvtClose(window);
  453. }
  454.  
  455. static void EvtMouseDownInZoom(EventRecord *event, WindowPtr window)
  456. {
  457.     Rect oldSize, newSize;
  458.     short part;
  459.     
  460.     /* need to call FindWindow after WinZoomPrepare */
  461.     WinZoomPrepare(window);
  462.     part = FindWindow(event->where, &window);
  463.     if (TrackBox(window, event->where, part)) {
  464.         WinPortRect(window, &oldSize);
  465.         WinZoom(window, part);
  466.         WinPortRect(window, &newSize);
  467.         WinResize(window, RectWidth(&newSize) - RectWidth(&oldSize),
  468.                                 RectHeight(&newSize) - RectHeight(&oldSize));
  469.     }
  470. }
  471.  
  472. /* dispatch a mouseDown event to the appropriate function */
  473. static void EvtMouseDown(EventRecord *event)
  474. {
  475.     WindowPtr window = NULL;
  476.     
  477.     ScreenSet(ScreenContainingPt(event->where));
  478.     switch (FindWindow(event->where, &window)) {
  479.     case inMenuBar:    EvtMouseDownInMenuBar(event); break;
  480.     case inSysWindow:    EvtMouseDownInSystemWindow(event, window); break;
  481.     case inContent:    EvtMouseDownInContent(event, window); break;
  482.     case inDrag:        EvtMouseDownInDrag(event, window); break;
  483.     case inGrow:        EvtMouseDownInGrow(event, window); break;
  484.     case inGoAway:        EvtMouseDownInGoAway(event, window); break;
  485.     case inZoomIn:        EvtMouseDownInZoom(event, window); break;
  486.     case inZoomOut:    EvtMouseDownInZoom(event, window); break;
  487.     }
  488. }
  489.  
  490. static void EvtMouseUp(EventRecord *event)
  491. {
  492.     ScreenSet(ScreenContainingPt(event->where));
  493. }
  494.  
  495. /*----------------------------------------------------------------------------*/
  496. /* keydown and autokey */
  497. /*----------------------------------------------------------------------------*/
  498.  
  499. /* handle command key events */
  500. static void EvtKeyDownCommand(EventRecord *event, unsigned char key)
  501. {
  502.     EvtClicked(FocusWindow(), NULL, 0);
  503.     EvtAdjustMenu();
  504.     if ((event->modifiers & cmdKey) != 0) {
  505.         long menu = MenuKey(key);
  506.         if (HiWord(menu)) {
  507.             MenuPickType pick = MenuPick(HiWord(menu), LoWord(menu));
  508.             EvtMenu(&pick);
  509.             HiliteMenu(0);
  510.         }
  511.     }
  512.     else
  513.         EvtMenu(MenuCmdPick(KeyToCmd(event)));
  514. }
  515.  
  516. /* handle all other keyDown events */
  517. static void EvtKeyDownOther(EventRecord *event)
  518. {
  519.     const EventTableType *table = FocusTable();
  520.  
  521.     if (table && table->focusObject.keydown) {
  522.         table->focusObject.keydown(FocusObject(), event);
  523.         EvtClicked(FocusWindow(), FocusObject(), FocusID());
  524.     }
  525. }
  526.  
  527. /* handle all keyDown events */
  528. static void EvtKeyDown(EventRecord *event)
  529. {
  530.     unsigned char key = event->message;
  531.     
  532.     if (! SystemIsActive()) {
  533.         ScreenSet(ScreenContainingWindow(FocusWindow()));
  534.         if ((event->modifiers & cmdKey) != 0 || KeyToCmd(event) != CMD_NONE) {
  535.             if (event->what != autoKey)
  536.                 EvtKeyDownCommand(event, key);
  537.         }
  538.         else if (key == tabKey && FocusWindow() && WinHasTabList(FocusWindow())) {
  539.             WinTab(FocusWindow());
  540.             EvtClicked(FocusWindow(), FocusObject(), FocusID());
  541.         }
  542.         else
  543.             EvtKeyDownOther(event);
  544.     }
  545. }
  546.  
  547. /* handle autoKey events */
  548. static void EvtAutoKey(EventRecord *event)
  549. {
  550.     if ((event->modifiers & cmdKey) == 0)
  551.         EvtKeyDown(event);
  552. }
  553.  
  554. /*----------------------------------------------------------------------------*/
  555. /* disk inserted event */
  556. /*----------------------------------------------------------------------------*/
  557.  
  558. /* handle a disk inserted event; from new "Inside Macintosh: Files", p5-10 */
  559. static void EvtDisk(EventRecord *event)
  560. {
  561.     Point pt = { 120, 120 };
  562.     
  563.     if (HiWord(event->message) == noErr) {
  564.         DILoad();
  565.         (void) DIBadMount(pt, event->message);
  566.         DIUnload();
  567.     }
  568. }
  569.  
  570. /*----------------------------------------------------------------------------*/
  571. /* operating system events */
  572. /*----------------------------------------------------------------------------*/
  573.  
  574. /* handle a mouse moved event */
  575. static void EvtOSMouseMoved(EventRecord *event)
  576. {
  577.     /* do nothing for now */
  578. }
  579.  
  580. /* handle a suspend event */
  581. static void EvtOSSuspend(void)
  582. {
  583.     const EventTableType *table;
  584.     short i, ntable;
  585.     
  586.     ntable = EventTableCount();
  587.     for (i = 0; i < ntable; i++) {
  588.         table = EventTableGet(i);
  589.         if (table->objectType.suspend)
  590.             table->objectType.suspend();
  591.     }
  592.     gInBackground = true;
  593. }
  594.  
  595. /* handle a resume event */
  596. static void EvtOSResume(void)
  597. {
  598.     const EventTableType *table;
  599.     short i, ntable;
  600.     
  601.     gInBackground = false;
  602.     ntable = EventTableCount();
  603.     for (i = 0; i < ntable; i++) {
  604.         table = EventTableGet(i);
  605.         if (table->objectType.resume)
  606.             table->objectType.resume();
  607.     }
  608. }
  609.  
  610. /* the desk scrap has changed */
  611. static void EvtOSConvertClipboard(void)
  612. {
  613.     /* do nothing for now */
  614. }
  615.  
  616. /* dispatch an operating system event */
  617. static void EvtOS(EventRecord *event)
  618. {
  619.     switch (HiByte(HiWord(event->message))) {
  620.     case mouseMovedMessage:
  621.         EvtOSMouseMoved(event);
  622.         break;
  623.     case suspendResumeMessage:
  624.         if (event->message & resumeFlag)
  625.             EvtOSResume();
  626.         else
  627.             EvtOSSuspend();
  628.         if (event->message & convertClipboardFlag)
  629.             EvtOSConvertClipboard();
  630.         break;
  631.     }
  632. }
  633.  
  634. /*----------------------------------------------------------------------------*/
  635. /* update and activate events */
  636. /*----------------------------------------------------------------------------*/
  637.  
  638. /* update all objects contained in the window specified in the event */
  639. static void EvtUpdate(EventRecord *event)
  640. {
  641.     WindowPtr window = (WindowPtr) event->message;
  642.     EventType *list = NULL;
  643.     
  644.     if (WinHasExtra(window)) {
  645.         BeginUpdate(window);
  646.         for (list = WinObjects(window); list; list = EventNext(list)) {
  647.             if (list->table->window.update)
  648.                 list->table->window.update(list->object);
  649.         }
  650.         EndUpdate(window);
  651.     }
  652. }
  653.  
  654. /* update the objects' focus */
  655. static void EvtFocus(EventType *focus, Boolean flag)
  656. {
  657.     if (focus->table->focusWindow.focus)
  658.         focus->table->focusWindow.focus(focus->object, flag);
  659. }
  660.  
  661. /* activate or deactivate all objects contained in the window */
  662. static void EvtActivate(WindowPtr window, Boolean activate)
  663. {
  664.     EventType *list;
  665.     
  666.     if (! activate)
  667.         EvtClicked(window, NULL, 0);
  668.     if (WinFocus(window))
  669.         EvtFocus(WinFocus(window), activate);
  670.     for (list = WinObjects(window); list; list = EventNext(list)) {
  671.         if (list->table->window.activate)
  672.             list->table->window.activate(list->object, activate);
  673.     }
  674. }
  675.  
  676. /* handle an activate or deactivate event */
  677. static void EvtActive(EventRecord *event)
  678. {
  679.     WindowPtr window = (WindowPtr) event->message;
  680.     Boolean activate = (event->modifiers & activeFlag) != 0;
  681.  
  682.     if (WinHasExtra(window)) {
  683.         if (WinLayer(window) == WIN_LAYER_FLOAT)
  684.             window = WinNextVisibleNonFloat(window);
  685.         if (window) {
  686.             EvtActivate(window, activate);
  687.             if (activate) {
  688.                 FocusWindowSet(window);
  689.                 EvtAdjustMenu();
  690.             }
  691.             else if (! GetCurActivate() || ! WinHasExtra(GetCurActivate())) {
  692.                 FocusWindowSet(NULL);
  693.                 EvtAdjustMenu();
  694.             }
  695.         }
  696.     }
  697. }
  698.  
  699. /*----------------------------------------------------------------------------*/
  700. /* high-level event */
  701. /*----------------------------------------------------------------------------*/
  702.  
  703. static void EvtHighLevel(EventRecord *event)
  704. {
  705.     Boolean oldAppleEvent;
  706.  
  707.     oldAppleEvent = gEvent.appleEvent;
  708.     gEvent.appleEvent = true;
  709.     (void) AEProcessAppleEvent(event);
  710.     gEvent.appleEvent = oldAppleEvent;
  711. }
  712.  
  713. /*----------------------------------------------------------------------------*/
  714. /* pre- and post- event filters */
  715. /*----------------------------------------------------------------------------*/
  716.  
  717. /* Do any pre-event filtering. This is useful for disabling certain events,
  718.     converting one type of event into a different event, or just handling
  719.     an event before the regular processing takes place (for instance,
  720.     to interpret command keys without calling MenuKey). */
  721. static void EvtFilterPre(EventRecord *event)
  722. {
  723.     EventType *list;
  724.     
  725.     for (list = FocusList(); list; list = EventNext(list)) {
  726.         if (list->table->focusWindow.prefilter)
  727.             list->table->focusWindow.prefilter(list->object, event);
  728.     }
  729. }
  730.  
  731. /* do any post-event processing */
  732. static void EvtFilterPost(EventRecord *event)
  733. {
  734.     EventType *list;
  735.  
  736.     for (list = FocusList(); list; list = EventNext(list)) {
  737.         if (list->table->focusWindow.postfilter)
  738.             list->table->focusWindow.postfilter(list->object, event);
  739.     }
  740. }
  741.  
  742. /*----------------------------------------------------------------------------*/
  743. /* executing events */
  744. /*----------------------------------------------------------------------------*/
  745.  
  746. /* execute the event */
  747. static void EvtExecute(EventRecord *event)
  748. {
  749.     EvtFilterPre(event);
  750.     switch (event->what) {
  751.     case nullEvent:        EvtNullEvent(event);    break;
  752.     case mouseDown:        EvtMouseDown(event);    break;
  753.     case mouseUp:            EvtMouseUp(event);    break;
  754.     case keyDown:            EvtKeyDown(event);    break;
  755.     case autoKey:            EvtAutoKey(event);    break;
  756.     case updateEvt:        EvtUpdate(event);        break;
  757.     case activateEvt:        EvtActive(event);        break;
  758.     case diskEvt:            EvtDisk(event);        break;
  759.     case osEvt:                EvtOS(event);            break;
  760.     case kHighLevelEvent:EvtHighLevel(event);    break;
  761.     }
  762.     EvtFilterPost(event);
  763. }
  764.  
  765. /* get and execute one event */
  766. static void EvtGetAndExecuteOne(void)
  767. {
  768.     EventRecord event;
  769.     
  770.     EventPreTasksExecute();
  771.     EventGet(everyEvent, &event, EvtAdjustSleep(), EvtAdjustCursor());
  772.     EvtExecute(&event);
  773.     EventPostTasksExecute();
  774. }
  775.  
  776. /*----------------------------------------------------------------------------*/
  777. /* external interface */
  778. /*----------------------------------------------------------------------------*/
  779.  
  780. /*----------------------------------------------------------------------------*/
  781. /* executing events */
  782. /*----------------------------------------------------------------------------*/
  783.  
  784. void EventMenu(const MenuPickType *pick)
  785. {
  786.     EvtMenu(pick);
  787. }
  788.  
  789. void EventAdjustMenu(void)
  790. {
  791.     EvtAdjustMenu();
  792. }
  793.  
  794. void EventFocus(EventType *focus, Boolean flag)
  795. {
  796.     EvtFocus(focus, flag);
  797. }
  798.  
  799. void EventActivate(WindowPtr window, Boolean activate)
  800. {
  801.     EvtActivate(window, activate);
  802. }
  803.  
  804. void EventExecute(EventRecord *event)
  805. {
  806.     EvtExecute(event);
  807. }
  808.  
  809. void EventOne(void)
  810. {
  811.     EvtGetAndExecuteOne();
  812. }
  813.  
  814. /*----------------------------------------------------------------------------*/
  815. /* event loops */
  816. /*----------------------------------------------------------------------------*/
  817.  
  818. /* handle an error in the main event loop */
  819. static void EvtErrorMain(void)
  820. {
  821.     OSErr err;
  822.     
  823.     TRY {
  824.         MemCanFail(false);
  825.         SegmentsUnloadEnable(true);
  826.         err = FailReason();
  827.         FailDisplay();
  828.         FailClear();
  829.         if (err == memFullErr) {
  830.             /* unload segments so as much memory as possible is free and
  831.                 the heap is as compact as possible so that the application
  832.                 can try again */
  833.             SegmentsUnload();
  834.         }
  835.         HiliteMenu(0);
  836.         MemCanFail(true);
  837.     } CATCH {
  838.         /* This gets executed if, while trying to recover from an
  839.             error, another error occurs. If this handler wasn't
  840.             present the application would simply exit. Often, the
  841.             second exception occurs when there's no more memory
  842.             to display an error alert. Instead of just exiting
  843.             (and losing unsaved files), we reset the error
  844.             condition and try to reenter the event loop. Should
  845.             a third exception occur while trying to recover
  846.             from the first two then the program will exit. */
  847.         FailClear();
  848.         HiliteMenu(0);
  849.         MemCanFail(true);
  850.         NOPROPAGATE;
  851.     } ENDTRY;
  852. }
  853.  
  854. /* run the event loop until it is exited (event loops may be nested
  855.     recursively) */
  856. void EventLoop(void)
  857. {
  858.     while (! gEvent.exit)
  859.         EvtGetAndExecuteOne();
  860.     gEvent.exit = false;
  861. }
  862.  
  863. /* the main event loop of the application */
  864. static void EvtLoopMain(void)
  865. {
  866.     TRY {
  867.         while (! gEvent.exit) {
  868.             DefragmentHeap();
  869.             EvtMemoryCheck();
  870.             EvtGetAndExecuteOne();
  871.         }
  872.         EvtCloseAll(FrontWindow());
  873.     } CATCH {
  874.         EvtErrorMain();
  875.         RETRY;
  876.     } ENDTRY;
  877. }
  878.  
  879. /* Call this after initialization to enter the main event loop.
  880.     This event loop can not be nested. */
  881. void EventLoopMain(void)
  882. {
  883.     EvtLoopMain();
  884. }
  885.  
  886. /* set a flag which will cause the current event loop to exit */
  887. void EventLoopExit(Boolean flag)
  888. {
  889.     gEvent.exit = flag;
  890. }
  891.  
  892. /*----------------------------------------------------------------------------*/
  893. /* utility functions */
  894. /*----------------------------------------------------------------------------*/
  895.  
  896. /* true if the application is in the background */
  897. Boolean EventInBackground(void)
  898. {
  899.     return(gInBackground);
  900. }
  901.  
  902. /* true if the application is executing an Apple Event */
  903. Boolean EventInAppleEvent(void)
  904. {
  905.     return(gEvent.appleEvent);
  906. }
  907.  
  908. /* Start interacting with the user. Call this before creating a dialog
  909.     or an alert that requires user interaction. */
  910. void EventInteractWithUser(void)
  911. {
  912.     if (EventInAppleEvent())
  913.         FailOSErr(AEInteractWithUser(kAEDefaultTimeout, NULL, NULL));
  914. }
  915.  
  916. /* Call GetNextEvent or WaitNextEvent, depending on which one is available.
  917.     The parameters to this function are identical to those to WaitNextEvent.
  918.     If GetNextEvent is called the extra parameters are ignored. */
  919. Boolean EventGet(short mask, EventRecord *event, TicksType sleep, RgnHandle cursor)
  920. {
  921.     Boolean result = false;
  922.  
  923.     if (MacHasWNE())
  924.         result = WaitNextEvent(mask, event, sleep, cursor);
  925.     else {
  926.         SystemTask();
  927.         result = GetNextEvent(mask, event);
  928.     }
  929.     if (! result) {
  930.         /* make sure it's a null event, even if the system thinks otherwise, e.g.,
  931.             some desk accessory events (see comment in TranSkell event loop) */
  932.         event->what = nullEvent;
  933.     }
  934.     return(result);
  935. }
  936.